import { SceneElement } from './SceneElement';
import { Vector2 } from '../../types/MathTypes';
import { TimelineState } from '../../types/TimelineTypes';
import { Logger } from '../../utils/Logger';

/**
 * BackgroundElement Class
 * 
 * Represents a background element in the scene.
 */
export class BackgroundElement extends SceneElement {
  private imageUrl: string;
  private image: any; // Will be HTMLImageElement or Canvas Image
  private width: number;
  private height: number;
  private imageLoaded: boolean = false;
  private imageLoading: boolean = false;
  projectId: string | null = null;

  /**
   * Creates an instance of BackgroundElement.
   * @param id - Unique identifier
   * @param imageUrl - URL of the background image
   * @param position - Position coordinates (x, y)
   * @param width - Width of the background
   * @param height - Height of the background
   * @param scale - Scale factors (x, y)
   * @param rotation - Rotation in degrees
   * @param visible - Visibility flag
   * @param zIndex - Z-order for rendering
   */
  constructor(
    id: string,
    imageUrl: string,
    position: Vector2 = { x: 0, y: 0 },
    width: number = 800,
    height: number = 600,
    scale: Vector2 = { x: 1, y: 1 },
    rotation: number = 0,
    visible: boolean = true,
    zIndex: number = -1, // Background typically has the lowest z-index
    projectId: string | null = null // Project ID for resolving asset paths
  ) {
    super(id, position, scale, rotation, visible, zIndex);
    this.projectId = projectId;
    this.width = width;
    this.height = height;
    
    const logger = Logger.getInstance();
    logger.log(`Creating background element ${id}`, 'BackgroundElement');
    logger.log(`Initial imageUrl: ${imageUrl}`, 'BackgroundElement');
    logger.log(`projectId: ${projectId}`, 'BackgroundElement');
    
    // Resolve background ID to actual image path if needed
    let resolvedImageUrl = imageUrl;
    logger.log(`Processing imageUrl: ${imageUrl}`, 'BackgroundElement');
    
    if (imageUrl && !imageUrl.includes('.')) {
      // This looks like a background ID, not a file path
      resolvedImageUrl = this.resolveBackgroundIdToPath(imageUrl, projectId);
      logger.log(`Resolved background ID to path: ${resolvedImageUrl}`, 'BackgroundElement');
    }
    
    // If we have a project ID, check if we need to load background data from a file
    if (projectId && resolvedImageUrl) {
      logger.log(`Checking for project-based resolution, resolvedImageUrl: ${resolvedImageUrl}`, 'BackgroundElement');
      if (resolvedImageUrl.endsWith('.json')) {
        try {
          const path = require('path');
          const fs = require('fs');
          
          // Construct the full path to the background file
          const workspacesDir = path.join(__dirname, '../../../workspaces');
          const backgroundFilePath = path.join(workspacesDir, projectId, 'assets', 'backgrounds', path.basename(resolvedImageUrl));
          
          logger.log(`Looking for background JSON file at: ${backgroundFilePath}`, 'BackgroundElement');
          logger.log(`File exists: ${fs.existsSync(backgroundFilePath)}`, 'BackgroundElement');
          // Read and parse the background file
          if (fs.existsSync(backgroundFilePath)) {
            const backgroundFileContent = fs.readFileSync(backgroundFilePath, 'utf8');
            const backgroundData = JSON.parse(backgroundFileContent);
            logger.log(`Loaded background JSON data with keys: ${Object.keys(backgroundData).join(', ')}`, 'BackgroundElement');
            
            // Use the first layer's file path as the main background image
            if (backgroundData.layers && backgroundData.layers.length > 0) {
              const firstLayer = backgroundData.layers[0];
              logger.log(`First layer: ${JSON.stringify(firstLayer)}`, 'BackgroundElement');
              if (firstLayer.filePath) {
                resolvedImageUrl = path.join(workspacesDir, projectId, 'assets', 'backgrounds', firstLayer.filePath);
                logger.log(`Using first layer filePath: ${resolvedImageUrl}`, 'BackgroundElement');
              } else {
                // Fallback to the file path itself with .png extension
                resolvedImageUrl = backgroundFilePath.replace('.json', '.png');
                logger.log(`Using fallback path (JSON->PNG): ${resolvedImageUrl}`, 'BackgroundElement');
              }
            } else {
              // Fallback to the file path itself with .png extension
              resolvedImageUrl = backgroundFilePath.replace('.json', '.png');
              logger.log(`Using fallback path (no layers): ${resolvedImageUrl}`, 'BackgroundElement');
            }
          } else {
            logger.warn(`Background JSON file not found: ${backgroundFilePath}`, 'BackgroundElement');
            // Try direct image path
            const directImagePath = path.join(workspacesDir, projectId, 'assets', 'backgrounds', path.basename(resolvedImageUrl, '.json') + '.png');
            logger.log(`Trying direct image path: ${directImagePath}`, 'BackgroundElement');
            logger.log(`Direct image exists: ${fs.existsSync(directImagePath)}`, 'BackgroundElement');
            if (fs.existsSync(directImagePath)) {
              resolvedImageUrl = directImagePath;
              logger.log(`Using direct image path: ${resolvedImageUrl}`, 'BackgroundElement');
            }
          }
        } catch (error) {
          logger.error(`Error loading background file for ${id}: ${(error as Error).message}`, 'BackgroundElement');
        }
      } else if (!resolvedImageUrl.includes('.') && !resolvedImageUrl.startsWith('workspaces')) {
        // Handle background ID case
        try {
          const path = require('path');
          const fs = require('fs');
          
          const workspacesDir = path.join(__dirname, '../../workspaces');
          const directImagePath = path.join(workspacesDir, projectId, 'assets', 'backgrounds', resolvedImageUrl + '.png');
          logger.log(`Trying direct image path from ID: ${directImagePath}`, 'BackgroundElement');
          logger.log(`Direct image from ID exists: ${fs.existsSync(directImagePath)}`, 'BackgroundElement');
          if (fs.existsSync(directImagePath)) {
            resolvedImageUrl = directImagePath;
            logger.log(`Resolved background ID to direct image path: ${resolvedImageUrl}`, 'BackgroundElement');
          } else {
            // Also try with .jpg extension
            const directImagePathJpg = path.join(workspacesDir, projectId, 'assets', 'backgrounds', resolvedImageUrl + '.jpg');
            logger.log(`Trying direct image path with .jpg: ${directImagePathJpg}`, 'BackgroundElement');
            logger.log(`Direct .jpg image exists: ${fs.existsSync(directImagePathJpg)}`, 'BackgroundElement');
            if (fs.existsSync(directImagePathJpg)) {
              resolvedImageUrl = directImagePathJpg;
              logger.log(`Resolved background ID to direct image path: ${resolvedImageUrl}`, 'BackgroundElement');
            }
          }
        } catch (error) {
          logger.error(`Error resolving background ID for ${id}: ${(error as Error).message}`, 'BackgroundElement');
        }
      }
    }
    
    logger.log(`Final resolved image URL for ${id}: ${resolvedImageUrl}`, 'BackgroundElement');
    this.imageUrl = resolvedImageUrl;
    
    if (this.imageUrl) {
      this.loadImage(this.imageUrl);
    }
  }

  /**
   * Resolves a background ID to an actual file path
   * @param backgroundId - The background ID to resolve
   * @param projectId - The project ID (optional)
   * @returns string - The resolved file path
   */
  private resolveBackgroundIdToPath(backgroundId: string, projectId: string | null = null): string {
    // Use the provided project ID
    const actualProjectId = projectId;
    console.log(`Resolving background ID '${backgroundId}' to file path for project '${actualProjectId}'`);
    
    // If we have a project ID, resolve the path within the project workspace
    if (actualProjectId) {
      return `${backgroundId}.json`;
    }
    
    // Fallback to default path
    return `assets/backgrounds/${backgroundId}.png`;
  }

  /**
   * Loads an image from URL
   * @param imageUrl - URL of the image to load
   */
  private async loadImage(imageUrl: string): Promise<void> {
    if (this.imageLoading) {
      return;
    }

    this.imageLoading = true;
    
    try {
      // Try to use node-canvas loadImage if available (for Node.js environment)
      try {
        // For exported videos, we're in a Node.js environment
        const path = require('path');
        const fs = require('fs');
        
        console.log(`Checking if image file exists: ${imageUrl}`);
        // Check if imageUrl is a file path
        if (fs.existsSync(imageUrl)) {
          const { loadImage } = require('canvas');
          console.log(`Loading image with node-canvas from path: ${imageUrl}`);
          this.image = await loadImage(imageUrl);
          this.imageLoaded = true;
          console.log(`Successfully loaded image from ${imageUrl}`);
        } else {
          // Try alternative paths
          console.log(`Image file not found at: ${imageUrl}`);
          
          // If this is a relative path, try to resolve it relative to the project
          if (imageUrl.startsWith('assets/backgrounds/')) {
            const fullPath = path.join(__dirname, '../../workspaces/sample-dialogue-project', imageUrl);
            console.log(`Trying alternative path: ${fullPath}`);
            if (fs.existsSync(fullPath)) {
              const { loadImage } = require('canvas');
              console.log(`Loading image with node-canvas from path: ${fullPath}`);
              this.image = await loadImage(fullPath);
              this.imageLoaded = true;
              console.log(`Successfully loaded image from ${fullPath}`);
              return;
            }
          }
          
          // If this is just a filename, try to find it in the backgrounds directory
          if (!imageUrl.includes('/') && !imageUrl.includes('\\')) {
            const backgroundsPath = path.join(__dirname, '../../workspaces/sample-dialogue-project/assets/backgrounds', imageUrl);
            console.log(`Trying backgrounds directory path: ${backgroundsPath}`);
            if (fs.existsSync(backgroundsPath)) {
              const { loadImage } = require('canvas');
              console.log(`Loading image with node-canvas from path: ${backgroundsPath}`);
              this.image = await loadImage(backgroundsPath);
              this.imageLoaded = true;
              console.log(`Successfully loaded image from ${backgroundsPath}`);
              return;
            }
          }
          
          console.warn(`Image file not found at any attempted location: ${imageUrl}`);
        }
      } catch (nodeCanvasError) {
        console.error('Error with node-canvas loading:', nodeCanvasError);
        // Fallback for browser environment or when canvas is not available
        console.warn('Node-canvas not available, using standard Image');
        if (typeof window !== 'undefined' && window.Image) {
          // Browser environment
          this.image = new window.Image();
          this.image.onload = () => {
            this.imageLoaded = true;
            this.imageLoading = false;
            console.log(`Successfully loaded image from ${imageUrl}`);
          };
          this.image.onerror = () => {
            console.error(`Failed to load image from ${imageUrl}`);
            this.imageLoading = false;
          };
          this.image.src = imageUrl;
        } else {
          // Neither node-canvas nor browser Image available
          console.warn(`Could not load image from ${imageUrl}: no image loading method available`);
          this.imageLoading = false;
          return;
        }
      }
    } catch (error) {
      console.error(`Failed to load image from ${imageUrl}:`, error);
      this.imageLoaded = false;
    } finally {
      if (this.image && typeof this.image.complete !== 'undefined' && this.image.complete) {
        // If image was loaded synchronously
        this.imageLoading = false;
        console.log(`Image loading completed for ${imageUrl}`);
      } else if (this.imageLoaded) {
        this.imageLoading = false;
        console.log(`Image loading completed for ${imageUrl}`);
      }
    }
  }

  /**
   * Gets the background image URL
   * @returns string - The image URL
   */
  public getImageUrl(): string {
    return this.imageUrl;
  }

  /**
   * Sets the background image URL
   * @param imageUrl - The new image URL
   */
  public async setImageUrl(imageUrl: string): Promise<void> {
    this.imageUrl = imageUrl;
    await this.loadImage(imageUrl);
  }

  /**
   * Gets the width
   * @returns number - The width
   */
  public getWidth(): number {
    return this.width;
  }

  /**
   * Sets the width
   * @param width - The new width
   */
  public setWidth(width: number): void {
    this.width = width;
  }

  /**
   * Gets the height
   * @returns number - The height
   */
  public getHeight(): number {
    return this.height;
  }

  /**
   * Sets the height
   * @param height - The new height
   */
  public setHeight(height: number): void {
    this.height = height;
  }

  /**
   * Checks if the image is loaded
   * @returns boolean - True if image is loaded
   */
  public isImageLoaded(): boolean {
    return this.imageLoaded;
  }

  /**
   * Renders the background to the canvas
   * @param canvasContext - The canvas context to render to
   */
  public render(canvasContext: any): void {
    const logger = Logger.getInstance();
    logger.log(`Rendering BackgroundElement ${this.id}, visible: ${this.visible}`, 'BackgroundElement');
    console.log(`[BackgroundElement] Rendering BackgroundElement ${this.id}, visible: ${this.visible}`);
    
    if (!this.visible) {
      logger.log(`Skipping rendering of ${this.id} because it is not visible`, 'BackgroundElement');
      console.log(`[BackgroundElement] Skipping rendering of ${this.id} because it is not visible`);
      return;
    }

    canvasContext.save();
    logger.log(`BackgroundElement ${this.id} context saved`, 'BackgroundElement');
    console.log(`[BackgroundElement] BackgroundElement ${this.id} context saved`);

    try {
      // Apply transforms
      canvasContext.translate(this.position.x, this.position.y);
      canvasContext.rotate(this.rotation * Math.PI / 180);
      canvasContext.scale(this.scale.x, this.scale.y);
      logger.log(`BackgroundElement ${this.id} transform applied: position=(${this.position.x}, ${this.position.y}), rotation=${this.rotation}, scale=(${this.scale.x}, ${this.scale.y})`, 'BackgroundElement');
      console.log(`[BackgroundElement] BackgroundElement ${this.id} transform applied: position=(${this.position.x}, ${this.position.y}), rotation=${this.rotation}, scale=(${this.scale.x}, ${this.scale.y})`);

      // Render background image if available
      if (this.image && this.imageLoaded && this.image.width > 0 && this.image.height > 0) {
        logger.log(`BackgroundElement ${this.id} has image source: ${this.imageUrl}`, 'BackgroundElement');
        console.log(`[BackgroundElement] BackgroundElement ${this.id} has image source: ${this.imageUrl}`);
        
        // Draw the image
        canvasContext.drawImage(this.image, -this.width/2, -this.height/2, this.width, this.height);
        logger.log(`BackgroundElement ${this.id} drew background image`, 'BackgroundElement');
        console.log(`[BackgroundElement] BackgroundElement ${this.id} drew background image`);
      } else {
        logger.log(`BackgroundElement ${this.id} has no valid image, skipping render`, 'BackgroundElement');
        console.log(`[BackgroundElement] BackgroundElement ${this.id} has no valid image, skipping render`);
        // Do not render placeholder
      }
    } catch (error) {
      logger.error(`Error rendering BackgroundElement ${this.id}: ${(error as Error).message}`, 'BackgroundElement');
      console.error(`[BackgroundElement] Error rendering BackgroundElement ${this.id}:`, error);
    } finally {
      canvasContext.restore();
      logger.log(`BackgroundElement ${this.id} context restored`, 'BackgroundElement');
      console.log(`[BackgroundElement] BackgroundElement ${this.id} context restored`);
    }
  }

  /**
   * Updates the element based on timeline state
   * @param timelineState - The current timeline state
   */
  public update(timelineState: TimelineState): void {
    // Background elements typically don't need updates based on timeline
    // But we could implement things like parallax effects here
  }
}